package com.auxiliary;

import java.util.ArrayList;
import java.util.List;

public class KLInformation {
	
	public static int order = 12;

	//总体计算
	public static CorrelationLine calc(List<Double> referenceList, List<Double> analysisList) {
		CorrelationLine corRes = new CorrelationLine();
		
		
		corRes.setData(calcKLInfCor(referenceList, analysisList));
		double max[] = findMin(corRes.getData());
		
		corRes.setEigenValue(max[0]);
		corRes.setEigenOrder((int)max[1]);
		
		return corRes;
	}
	
	//计算两个序列之间的交叉KL信息量
	private static List<Double> calcKLInfCor(List<Double> baseList, List<Double> analysisList) {
		int size = baseList.size() < analysisList.size() ? baseList.size() : analysisList.size();
        List<Double> allKL = new ArrayList<Double>();
        for (int i = -order; i < 0; i++)
        {
            List<Double> tempBaseList = new ArrayList<Double>();
            for (int j = 0; j < size + i; j++)
            {
                tempBaseList.add(baseList.get(j - i));
            }

            allKL.add(calcBaseKLCor(tempBaseList, analysisList));

        }
        for (int i = 0; i <= order; i++)
        {
            List<Double> tempAnalysisList = new ArrayList<Double>();
            for (int j = 0; j < size - i; j++)
            {
                tempAnalysisList.add(analysisList.get(i + j));
            }
            allKL.add(calcBaseKLCor(baseList, tempAnalysisList));

        }
		
		return allKL;
	}
	
	//计算两个序列之间的时差相关系数
	private static double calcBaseKLCor(List<Double> list1, List<Double> list2) {
		
		List<Double> list1AC = Converter.ConvertToGreaterThanZero(list1);
		List<Double> list2AC = Converter.ConvertToGreaterThanZero(list2);
		
		int size = list1AC.size() < list2AC.size() ? list1AC.size() : list2AC.size();
		
		double allQ, allP, kl;
		allQ = allP = kl = 0;
		
		for(int i = 0; i < size; i++) {
			allP += list1AC.get(i);
			allQ += list2AC.get(i);
		}
		
		for(int i = 0; i < size; i++) {
			double p = list1AC.get(i) / allP;
			double q = list2AC.get(i) / allQ;
			kl += p * Math.log(p / q);
		}
		
		return kl * 10000;
	}
	
	private static double[] findMin(List<Double> allKL)
    {
        int minOrder = -order;
        if (allKL == null)
        	return null;
        
        double minKLInformation = allKL.get(0);
        for (int j = -order + 1; j < allKL.size() - order; j++)
        {
            if (allKL.get(j + order) < minKLInformation)
            {
                minKLInformation = allKL.get(j + 12);
                minOrder = j;
            }
        }
        return new double[] { minKLInformation, minOrder };
    }
}
